<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>Document</title>
    <script src='lib/webgl-utils.js'></script>
    <script src='lib/webgl-debug.js'></script>
    <script src='lib/cuon-utils.js'></script>
    <script src="lib/cuon-matrix.js"></script>
    <style>
        *{
            margin:0;
            padding:0;
        }
    </style>
</head>
<body onload="main()">
<canvas id="mybox" width="500" height="500">
    请更换新版浏览器查看效果!
</canvas>
<script id="vertexShader" type="x-shader/x-vertex">
attribute vec4 a_position;
attribute vec4 a_color;
attribute vec4 a_normal;
uniform mat4 u_mvpMatrix;
uniform mat4 u_modelMatrix;
uniform mat4 u_normalMatrix;
varying vec4 v_color;
varying vec3 v_normal;
varying vec3 v_position;
void main(){
  gl_Position=u_mvpMatrix*a_position;
  v_position=vec3(u_modelMatrix*a_position);
  v_normal=normalize(vec3(u_normalMatrix*a_normal));
  v_color=a_color;
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
#ifdef GL_ES
precision mediump float;
#endif
uniform vec3 u_lightColor;
uniform vec3 u_lightPosition;
uniform vec3 u_ambientLight;
varying vec3 v_normal;
varying vec3 v_position;
varying vec4 v_color;
void main(){
  vec3 normal=normalize(v_normal);
  vec3 lightDirection=normalize(u_lightPosition-v_position);
  float nDotL=max(dot(lightDirection,normal),0.0);
  vec3 diffuse=u_lightColor*v_color.rgb*nDotL;
  vec3 ambient=u_ambientLight*v_color.rgb;
  gl_FragColor=vec4(diffuse+ambient,v_color);
}
</script>
<script >

    function main(){
        // init
        const canvas=document.getElementById('mybox');
        const gl=getWebGLContext(canvas);
        const VSHADER_SOURCE = document.getElementById( 'vertexShader' ).textContent;
        const FSHADER_SOURCE = document.getElementById( 'fragmentShader' ).textContent;
        if(!initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE)){
            console.log('初始化shader失败');
            return;
        }
        const n = initVertexBuffers(gl);
        if(n<0){
            console.log('failed to initialize shaders.');
            return;
        }
        gl.clearColor(0,0,0,1);
        gl.enable(gl.DEPTH_TEST);
        let u_mvpMatrix=gl.getUniformLocation(gl.program,'u_mvpMatrix');
        let u_modelMatrix=gl.getUniformLocation(gl.program,'u_modelMatrix');
        let u_normalMatrix=gl.getUniformLocation(gl.program,'u_normalMatrix');
        let u_lightColor=gl.getUniformLocation(gl.program,'u_lightColor');
        let u_ambientLight=gl.getUniformLocation(gl.program,'u_ambientLight');
        let u_lightPosition=gl.getUniformLocation(gl.program,'u_lightPosition');
        if (!u_mvpMatrix ||!u_modelMatrix ||!u_normalMatrix || !u_lightColor || !u_lightPosition || !u_ambientLight) {
            console.log('Failed to get the storage location');
            return;
        }
        gl.uniform3f(u_lightColor,1.0,1.0,1.0);
        gl.uniform3f(u_lightPosition,2.3,4.0,3.5);
        gl.uniform3f(u_ambientLight,0.2,0.2,0.2);

        let modelMatrix = new Matrix4();
        let mvpMatrix = new Matrix4();
        let normalMatrix = new Matrix4();
        // 相机位置  视点  上方向
        let cameraPosition = new Vector3([0, 0, 14]);
        let objectPosition = new Vector3([0, 0, 0]);
        let lookAtPosition = new Vector3([0,0,0]);
        let upVector = new Vector3([0, 1, 0]);
        mvpMatrix.setPerspective(100,canvas.width/canvas.height,1,1000);


        modelMatrix.setRotate(0,0,1,0);
        gl.uniformMatrix4fv(u_modelMatrix,false,modelMatrix.elements);
        mvpMatrix.lookAt(cameraPosition.elements[0], cameraPosition.elements[1], cameraPosition.elements[2],
            lookAtPosition.elements[0], lookAtPosition.elements[1], lookAtPosition.elements[2],
            upVector.elements[0], upVector.elements[1], upVector.elements[2]);
        mvpMatrix.multiply(modelMatrix);
        gl.uniformMatrix4fv(u_mvpMatrix,false,mvpMatrix.elements);

        normalMatrix.setInverseOf(modelMatrix);// 逆矩阵
        normalMatrix.transpose();// 转置
        gl.uniformMatrix4fv(u_normalMatrix,false,normalMatrix.elements);
        // 初始显示状态
        gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
        gl.drawElements(gl.TRIANGLES,n,gl.UNSIGNED_BYTE,0);

        // 键盘w a s d - 相机位置平移
        document.addEventListener('keydown',function(e){
            // 向右
            switch(e.keyCode){
                case 39:// 右
                case 68:
                    cameraPosition.elements[0] += 2;
                    break;
                case 37:// 左
                case 65:
                    cameraPosition.elements[0] -= 2;
                    break;
                case 38:// 上
                case 87:
                    cameraPosition.elements[1] += 2;
                    break;
                case 40:// 下
                case 83:
                    cameraPosition.elements[1] -= 2;
                    break;
                default:
                    break;
            }
            // 每次都需要重新设置perspective视场
            mvpMatrix.setPerspective(100,canvas.width/canvas.height,1,1000);
            mvpMatrix.lookAt(cameraPosition.elements[0], cameraPosition.elements[1], cameraPosition.elements[2],
                lookAtPosition.elements[0], lookAtPosition.elements[1], lookAtPosition.elements[2],
                upVector.elements[0], upVector.elements[1], upVector.elements[2]);
            gl.uniformMatrix4fv(u_mvpMatrix,false,mvpMatrix.elements);
            gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
            gl.drawElements(gl.TRIANGLES,n,gl.UNSIGNED_BYTE,0);
        });

        canvas.addEventListener('mousemove',function(e){
            // 鼠标移动时这个点在WebGL中的坐标
            let rect=e.target.getBoundingClientRect();
            //此时以canvas正中心的点为原点
            //canvas横坐标/webGL横坐标=canvas宽/2
            //webGL Y方向与canvas中相反
            let posX=((e.clientX - rect.left)-canvas.width/2)/(canvas.width/2);
            let posY=(canvas.height/2-(e.clientY - rect.top))/(canvas.height/2);
            if(Math.abs(posX) >= Math.abs(posY) ){
                // 绕Y轴旋转
                let angle =  90 * posX;
                let rotateAxis = new Vector3([0, 1, 0]);
                lookAtPosition = rotate(cameraPosition,lookAtPosition,angle,rotateAxis);
            } else {
                // 绕X轴旋转
                let angle = 90 * posY;
                let rotateAxis = new Vector3([1, 0, 0]);
                lookAtPosition = rotate(cameraPosition,lookAtPosition,angle,rotateAxis);
            }
            mvpMatrix.setPerspective(100,canvas.width/canvas.height,1,1000);
            mvpMatrix.lookAt(cameraPosition.elements[0], cameraPosition.elements[1], cameraPosition.elements[2],
                lookAtPosition.elements[0], lookAtPosition.elements[1], lookAtPosition.elements[2],
                upVector.elements[0], upVector.elements[1], upVector.elements[2]);
            gl.uniformMatrix4fv(u_mvpMatrix,false,mvpMatrix.elements);
            gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
            gl.drawElements(gl.TRIANGLES,n,gl.UNSIGNED_BYTE,0);

        });

    }

    function initVertexBuffers(gl){
        let vertices=new Float32Array([
            1.0, 1.0, 1.0,  -1.0, 1.0, 1.0,  -1.0,-1.0, 1.0,   1.0,-1.0, 1.0, // v0-v1-v2-v3 front
            1.0, 1.0, 1.0,   1.0,-1.0, 1.0,   1.0,-1.0,-1.0,   1.0, 1.0,-1.0, // v0-v3-v4-v5 right
            1.0, 1.0, 1.0,   1.0, 1.0,-1.0,  -1.0, 1.0,-1.0,  -1.0, 1.0, 1.0, // v0-v5-v6-v1 up
            -1.0, 1.0, 1.0,  -1.0, 1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0,-1.0, 1.0, // v1-v6-v7-v2 left
            -1.0,-1.0,-1.0,   1.0,-1.0,-1.0,   1.0,-1.0, 1.0,  -1.0,-1.0, 1.0, // v7-v4-v3-v2 down
            1.0,-1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0, 1.0,-1.0,   1.0, 1.0,-1.0  // v4-v7-v6-v5 back
        ]);
        let colors=new Float32Array([
            1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v1-v2-v3 front
            1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v3-v4-v5 right
            1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v5-v6-v1 up
            1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v1-v6-v7-v2 left
            1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v7-v4-v3-v2 down
            1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0　    // v4-v7-v6-v5 back
        ]);
        let normals=new Float32Array([
            0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,  // v0-v1-v2-v3 front
            1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,  // v0-v3-v4-v5 right
            0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,  // v0-v5-v6-v1 up
            -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  // v1-v6-v7-v2 left
            0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,  // v7-v4-v3-v2 down
            0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0   // v4-v7-v6-v5 back
        ]);
        let indices=new Uint8Array([
            0, 1, 2,   0, 2, 3,    // front
            4, 5, 6,   4, 6, 7,    // right
            8, 9,10,   8,10,11,    // up
            12,13,14,  12,14,15,    // left
            16,17,18,  16,18,19,    // down
            20,21,22,  20,22,23     // back
        ]);
        if (!initArrayBuffer(gl, 'a_color', colors, 3, gl.FLOAT)) {
            return -1;
        }
        if (!initArrayBuffer(gl, 'a_normal', normals, 3, gl.FLOAT)) {
            return -1;
        }
        if (!initArrayBuffer(gl, 'a_position', vertices, 3, gl.FLOAT)) {
            return -1;
        }
        let indexBuffer=gl.createBuffer();
        if(!indexBuffer){
            console.log('Failed to create the buffer object');
            return false;
        }
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indices,gl.STATIC_DRAW);

        return indices.length


    }
    function initArrayBuffer(gl,attribute,data,num,type){
        let buffer=gl.createBuffer();
        if(!buffer){
            console.log('Failed to create the buffer object');
            return false;
        }
        gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
        gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);

        let a_attribute=gl.getAttribLocation(gl.program,attribute);
        if (a_attribute < 0) {
            console.log('Failed to get the storage location of ' + attribute);
            return false;
        }
        gl.vertexAttribPointer(a_attribute,num,type,false,0,0);
        gl.enableVertexAttribArray(a_attribute);
        gl.bindBuffer(gl.ARRAY_BUFFER,null);
        return true;

    }

    // rotate 用于计算旋转后的视点坐标
    function rotate(cameraPosition,lookAtPosition,angle,rotateAxis){
        let matForRotate = new Matrix4();
        let camToObj = new Vector3([
            cameraPosition.elements[0] - lookAtPosition.elements[0],
            cameraPosition.elements[1] - lookAtPosition.elements[1],
            cameraPosition.elements[2] - lookAtPosition.elements[2],
        ]);
        matForRotate.setTranslate(cameraPosition.elements[0],cameraPosition.elements[1],cameraPosition.elements[2]);
        matForRotate.rotate(angle,rotateAxis.elements[0],rotateAxis.elements[1],rotateAxis.elements[2]);
        let newCamToObj = matForRotate.multiplyVector3(camToObj);
        let newlookAtPosition = new Vector3([
        newCamToObj.elements[0] + cameraPosition.elements[0],
        newCamToObj.elements[1] + cameraPosition.elements[1],
        newCamToObj.elements[2] + cameraPosition.elements[2],
        ]);
        return newlookAtPosition
    }

</script>

</body>
</html>